﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace FerrSimplicity
{
    public class Timer
    {
        #region Fields
        internal float  mDuration;
        internal float  mCurrTime;
        internal bool   mRepeat;
        internal object mArgument;
        private  bool   mReadFired;

        public delegate void TimerEvent(Timer aTimer, object aArgs);
        public event TimerEvent OnFire;
        #endregion

        #region Properties
        /// <summary>
        /// This property will be true for the frame during which it fired
        /// </summary>
        public bool  HasFired { get; private set; }
        /// <summary>
        /// Allows you to pause or unpause this timer.
        /// </summary>
        public bool  Active   { get; set; }

        /// <summary>
        /// How many seconds are still left on the timer.
        /// </summary>
        public float RemainingTime
        {
            get { return mCurrTime; }
        }
        /// <summary>
        /// What percentage of time has passed through this timer.
        /// </summary>
        public float PercentagePassed
        {
            get { return mCurrTime / mDuration; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Fires the timer, and triggers the appropriate events
        /// </summary>
        internal void Fire  ()
        {
            HasFired   = true;
            mReadFired = false;

            if (OnFire != null)
                OnFire(this, mArgument);
        }
        /// <summary>
        /// Updates the timer's internal data
        /// </summary>
        /// <param name="aTime">The amount of time that has passed since the last frame</param>
        /// <returns>True if the timer fire, false if it doesn't</returns>
        internal bool Update(float aTime)
        {
            if (Active)
            {
                mCurrTime -= aTime;

                if (mCurrTime <= 0)
                {
                    HasFired = true;

                    // call any events attached to this timer
                    Fire();

                    // loop it, or delete it, since we hit the end of the timer
                    if (mRepeat)
                        mCurrTime += mDuration;
                    else
                        return true;
                }
            }

            // delete the fired flag if it's been read
            if (mReadFired == true)
                HasFired = false;

            return false;
        }
        #endregion
    }
    public class SystemTime
    {
        #region Singleton
        private static SystemTime mInstance = null;
        public  static SystemTime Instance
        {
            get { if (mInstance == null) mInstance = new SystemTime(); return mInstance; }
        }
        private SystemTime()
        {
            mTimerList = new List<Timer>();
        }
        #endregion

        #region Fields
        private List<Timer> mTimerList;
        #endregion

        #region Properties
        /// <summary>
        /// The frames per second, smoothed a little, so it doesn't spike.
        /// </summary>
        public float FPS { get; private set; }
        #endregion

        #region Methods
        public void Update(GameTime aTime)
        {
            // update the FPS
            FPS = FPS * 0.2f + (1f / (float)aTime.ElapsedGameTime.TotalSeconds) * 0.8f;

            // update all attached timers
            for (int i = 0; i < mTimerList.Count; i++)
            {
                if (mTimerList[i].Update((float)aTime.ElapsedGameTime.TotalSeconds))
                {
                    mTimerList.RemoveAt(i);
                    i--;
                }
            }
        }

        /// <summary>
        /// Creates a timer with the given properties, and registers it in the system for updates
        /// </summary>
        /// <param name="aTime">How long until the timer fires</param>
        /// <param name="aRepeat">If it repeats againe once it has fired</param>
        public Timer AddTimer(float aTime)               { return AddTimer(aTime, false,   null, null); }
        /// <summary>
        /// Creates a timer with the given properties, and registers it in the system for updates
        /// </summary>
        /// <param name="aTime">How long until the timer fires</param>
        /// <param name="aRepeat">If it repeats againe once it has fired</param>
        /// <returns>The timer that gets registered to the system, just in case you need it for something</returns>
        public Timer AddTimer(float aTime, bool aRepeat) { return AddTimer(aTime, aRepeat, null, null); }
        /// <summary>
        /// Creates a timer with the given properties, and registers it in the system for updates
        /// </summary>
        /// <param name="aTime">How long until the timer fires</param>
        /// <param name="aRepeat">If it repeats againe once it has fired</param>
        /// <param name="aOnFire">The event it calls when it fires</param>
        /// <param name="aArgument">Any arguments that get sent to the event, if it's called</param>
        /// <returns>The timer that gets registered to the system, just in case you need it for something</returns>
        public Timer AddTimer(float aTime, bool aRepeat, Timer.TimerEvent aOnFire, object aArgument)
        {
            Timer newTimer = new Timer();
            newTimer.mArgument = aArgument;
            newTimer.mCurrTime = newTimer.mDuration = aTime;
            newTimer.mRepeat   = aRepeat;

            mTimerList.Add(newTimer);

            return newTimer;
        }
        /// <summary>
        /// Allows you to remove and unregister a timer from the system.
        /// </summary>
        /// <param name="aTimer">The timer you wish to remove.</param>
        public void  RemoveTimer(Timer aTimer)
        {
            mTimerList.Remove(aTimer);
        }
        /// <summary>
        /// Removes all timers from the system
        /// </summary>
        public void  ClearTimers()
        {
            mTimerList.Clear();
        }
        #endregion
    }
}
